
class MapNode:
    idx = 0
    def __init__(self, pos, parent):
        self.pos = pos.clone()
        self.idx = MapNode.idx
        self.accessible = True
        self.parent = parent
        MapNode.idx  += 1
        self.edge_list = []

class MapEdge:
    def __init__(self, dest, cost):
        self.dest = dest
        self.cost = cost

class NavGraph:
    def __init__(self):
        self.graph = None
        self.width = 0
        self.height = 0
        self.node_list = []
    def set_dimensions(self, width, height):
        """ Clears map """
        MapNode.idx = 0
        self.width = width
        self.height = height
        self.graph = []
        self.node_list = []
        for x in range(width):
            self.graph.append([])
            for y in range(height):
                node = MapNode(Vector(x, y), self)
                self.node_list.append(node)
                self.graph[x].append(node)
        for x in range(width):
            for y in range(height):
                node = self.graph[x][y]
                y_lt_b = y < len(self.graph[x]) - 1
                x_lt_b = x < len(self.graph) - 1
                if x > 0:
                    if y > 0:
                        node.edge_list.append(MapEdge(self.graph[x - 1][y - 1], 3))
                    node.edge_list.append(MapEdge(self.graph[x - 1][y], 2))
                    if y_lt_b:
                        node.edge_list.append(MapEdge(self.graph[x - 1][y + 1],3))
                if y > 0:
                    node.edge_list.append(MapEdge(self.graph[x][y - 1],2))
                if y_lt_b:
                    node.edge_list.append(MapEdge(self.graph[x][y + 1],2))
                if x_lt_b:
                    if y > 0:
                        node.edge_list.append(MapEdge(self.graph[x + 1][y - 1],3))
                    node.edge_list.append(MapEdge(self.graph[x + 1][y],2))
                    if y_lt_b:
                        node.edge_list.append(MapEdge(self.graph[x + 1][y + 1],3))
    def alter_accessibility(self, nodes):
        for x, y in nodes:
            self.graph[x][y].accessible = False
    def get_node(self, x, y):
        return self.graph[x][y]
    def is_path_obstructed(self, src, dest, radius):
        return True
    def get_closest_node_to_position(self, pos):
        # FIXME - this could be inside a wall
        x, y = pos
        x = int(x / 16)
        y = int(y / 16)
        return self.graph[x][y]

class MapData:
    def __init__(self, inaccessible_nodes_file, spawns, exit):
        surface = pygame.image.load(StringIO.StringIO(zlib.decompress(base64.b64decode(data[inaccessible_nodes_file]))),
                inaccessible_nodes_file)
        self.width = 40
        self.height = 30
        self.inaccessible_nodes = []
        for i in range(self.width):
            for j in range(self.height):
                r, g, b, w = surface.get_at((i, j))
                if r == 0:
                    self.inaccessible_nodes.append((i, j))
        self.spawns = spawns
        self.exit = exit
class Wall:
    def __init__(self, start, end ):
        self.normal = Vector(x = 1, y = 0)
        self.start = start
        self.end = end

class NodeWithCost:
    def __init__(self, parent, node, cost):
        self.parent = parent
        self.node = node
        self.cost = cost


class PathPlanner: 
    def __init__(self, owner, navGraph):
        self.owner = owner
        self.nav_graph = navGraph
    def CreatePathToPosition(self, targetPos):
        """ Returns tuple of if found, and the path """
        found = False
        path = []
        if not self.nav_graph.is_path_obstructed(self.owner.pos, targetPos, 5):
            return True, [targetPos]
        closest_node_to_target = self.nav_graph.get_closest_node_to_position(targetPos)
        if closest_node_to_target is None:
            return False, []
        closest_node_to_bot = self.nav_graph.get_closest_node_to_position(self.owner.pos.tuple())
        if closest_node_to_bot is None:
            return False, []
        return self.search(closest_node_to_bot, closest_node_to_target)

    def search(self, src, dest):
        pq = PriorityQueue()
        current = NodeWithCost(None, src, 0)
        pq.push(current)
        path = {}
        frontier = {}
        frontier[current.node.idx] = current
        last = None
        while len(pq) > 0:
            current = pq.pop()
            last = current
            path[current.node.idx] = current
            if current.node.idx == dest.idx:
                break
            for edge in current.node.edge_list:
                if not edge.dest.accessible:
                    continue
                node_with_cost = NodeWithCost(
                    current.node,
                    edge.dest,
                    current.cost + edge.cost # FIXME - dikstras
                )
                if not frontier.has_key(edge.dest.idx):
                    frontier[edge.dest.idx] = node_with_cost
                    pq.push(node_with_cost)
                elif node_with_cost.cost < frontier[edge.dest.idx].cost and \
                        not path.has_key(edge.dest.idx):
                    frontier[edge.dest.idx] = node_with_cost
                    pq.change(node_with_cost)
        actual_path = []
        actual_path.append(last.node)
        while last.node.idx != src.idx:
            last = path[last.parent.idx]
            actual_path.append(last.node)
        return actual_path

    def CreatePathToItem(self, itemType):
        """ Returns tuple of if found and the path """
        found = False
        path = []
        return found, path


class RocketSpawn:
    def __init__(self, pos):
        self.timer = 0
        self.pos = pos
        self.cached_pos = pos * 16
        self.has_rocket = False
        self.colour = (0, 0, 255)
    def Update(self, delta, engine):
        self.timer += delta
        if self.timer > 10000:
            self.has_rocket = True
    def Render(self, surface):
        pygame.draw.circle(surface, self.colour, self.cached_pos.tuple(), 8)
        if self.has_rocket:
            rocket_pos = self.cached_pos.tuple()
            rocket_pos = (rocket_pos[0], rocket_pos[1] + 5) 
            pygame.draw.rect(surface, (0, 0, 0), rocket_pos + (16, 2))

class SpawnPoint:
    def __init__(self, pos):
        self.pos = pos
        self.cached_pos = pos
        self.colour = (125, 125, 125)
        self.accrued = 0
    def Update(self, delta, engine):
        self.accrued += delta
        if self.accrued > 1000:
            engine.spawn_enemy(self.pos)
            self.accrued -= 1000
    def Render(self, surface):
        pygame.draw.circle(surface, self.colour, self.cached_pos.tuple(), 5)

class Exit:
    def __init__(self, pos):
        self.pos = pos
        self.cached_pos = pos 
        self.colour = (0, 255, 0)
    def Update(self, delta, engine):
        pass
    def Render(self, surface):
        pygame.draw.circle(surface, self.colour, self.cached_pos, 5)

